'''
本软件是由@hehung开发.
未经同意请勿转载和二次开发.
联系方式：
微信: hehung95
邮箱:1398660197@qq.com
'''

import tkinter as tk
import tkinter.font as tkFont
from tkinter import filedialog
import tkinter.messagebox
from tkinter import ttk
import os
import threading
import time
import serial
import serial.tools.list_ports

app_readme_text = """
本软件是用于RA4M2的hex文件下载工具 - 只支持UART
开发者 - hehung
任何问题可以联系开发者
wechat: hehung95
e-mail:1398660197@qq.com
"""

buadrate = 512000

class gui_class:
    def __init__(self):
        self.hexFileName = ''
        self.width = 540
        self.height = 300

        self.root = tk.Tk()  # 实例化TK 

        self.root.minsize(self.width, self.height)
        self.root.title('RA4M2 Hex Downloader | by hehung')
        self.root.resizable(width=False, height=False)

        self.LabelfontStyle = tkFont.Font(family = "Arial", size=20)
        self.BtnfontStyle = tkFont.Font(family = "仿宋", size=16)
        self.mainLabel = tk.Label(self.root, bg='green', 
                                             fg = 'white', 
                                             text = 'RA4M2 Hex File Downloader',
                                             font = self.LabelfontStyle)
        self.mainLabel.grid(row = 0, column = 0, columnspan = 2, ipadx=20, ipady=5, padx=10, pady=10, sticky='nswe') 

        self.FileEntryVar = tk.Variable()
        self.fileEntry = tk.Entry(self.root, background  = "White",
                                             textvariable = self.FileEntryVar)
        self.fileEntry.grid(row = 1, column = 0, columnspan = 2, ipadx=10, ipady=5, padx=10, pady=10, sticky='nswe') 

        self.fileSelBtn = tk.Button(self.root, text="选择hex文件", 
                                               width = 20,
                                               font = self.BtnfontStyle,
                                               command=self.gui_fileSelBtnCmdFb)
        self.fileSelBtn.grid(row = 2, column = 0, padx=10, pady=10, ipadx=10, ipady=2, sticky='nesw') 

        self.hexDwonloadBtn = tk.Button(self.root, text="下载", 
                                                   width = 20,
                                                   font = self.BtnfontStyle,  
                                                   command=self.gui_fileDownloadCmdFb)
        self.hexDwonloadBtn.grid(row=2, column=1, padx=10, pady=10, ipadx=10, ipady=2, sticky='nesw') 

        self.valProcessBar = tk.IntVar(value = 0)
        self.progressBar = ttk.Progressbar(self.root, variable = self.valProcessBar)
        self.progressBar.grid(row = 3, column = 0, padx=10, pady=10, columnspan = 2, sticky='nswe')

        self.ComLabel = tk.Label(self.root, text = '选择串口端口号', font = ("仿宋", 14), anchor="w", justify='right')
        self.ComLabel.grid(row = 4, column = 0,padx=10, pady=10, columnspan = 2, sticky='nswe')

        self.comboxlist = ttk.Combobox(self.root, textvariable = '选择COM', postcommand = self.gui_serialRefresh)
        self.comboxlist.grid(row = 4, column = 1, padx=10, pady=10, columnspan = 2, sticky='nswe')
        self.gui_serialRefresh()
        try:
            self.comboxlist.current(0)
        except:
            print ('没有连接任何串口')

        self.ComLabel = tk.Label(self.root, text = '波特率', font = ("仿宋", 14), anchor="w", justify='right')
        self.ComLabel.grid(row = 5, column = 0, padx=10, pady=10, columnspan = 2, sticky='nswe')
        self.ComBaudrateEntryVar = tk.Variable()
        self.ComBaudrateEntryVar.set(buadrate)
        self.ComEntry = tk.Entry(self.root, background  = "White",
                                             textvariable = self.ComBaudrateEntryVar)
        self.ComEntry.grid(row = 5, column = 1, ipadx=10, ipady=5, padx=10, pady=10, sticky='nswe')

        self.ReadmeLabel = tk.Label(self.root, text = app_readme_text, font = ("仿宋", 14), anchor="w", justify='left')
        self.ReadmeLabel.grid(row = 6, column = 0, rowspan = 2, padx=10, pady=10, columnspan = 2, sticky='nswe')

        self.hexFileLines = 0
        self.hexDownloadLine = 0

    def gui_running(self):
        self.root.mainloop()

    def gui_fileSelBtnCmdFb(self):
        self.hexFileName = tk.filedialog.askopenfilename(defaultextension='.hex', 
                                                         title='选择hex文件',
                                                         initialdir = './')
        print ("Opened the File " + self.hexFileName);
        self.FileEntryVar.set(self.hexFileName)

    def gui_serialRefresh(self):
        # 打开串口，找到端口号
        serial_app = serial_class()
        self.com_list = []
        self.com_dict = serial_app.serial_findPort()
        for key in self.com_dict.keys():
            com_str = str(key) + ': ' + self.com_dict[key]
            self.com_list.append(com_str)
            print(com_str)
        self.comboxlist["values"] = tuple(self.com_list)

    def gui_fileDownloadCmdFb(self):
        if (os.path.isfile(self.hexFileName)):
            with open(self.hexFileName, 'r') as hexfile:
                # 行数
                self.hexFileLines = len(hexfile.readlines())
                hexfile.close()
            # 开启线程用于下载和显示进度
            threading.Thread(target = self.gui_downloadHexAndPrase).start()
        else:
            tk.messagebox.showerror(title="Hex Error", message="所选文件不是一个hex文件")
            print ('选择的文件“' + self.hexFileName + '”不是一个hex文件或者路径有问题')

    def gui_downloadHexAndPrase(self):
        # 选择端口
        self.serial_port = self.comboxlist.get().split(':')[0]
        if (self.serial_port != ''):
            self.serial_buadrate = self.ComBaudrateEntryVar.get()
            print (self.serial_port, self.serial_buadrate)
            # 1s timeout
            try:
                with serial.Serial(self.serial_port, self.serial_buadrate, timeout = 1) as ser:
                    print ('serial ' + str(self.serial_port) + ' opened,' + 'buadrate: ' + str(self.serial_buadrate))
                    self.hexDownloadLine = 0
                    self.gui_downloadHex(self.hexFileName, ser)
            except Exception as e:
                tk.messagebox.showerror(title="端口错误", message=str(self.serial_port) + "被占用")
                print (str(self.serial_port) + '所选端口被占用. Err: ' + str(e))
        else:
            tk.messagebox.showwarning(title="串口未选择", message="未选择串口端口或串口断开")
            print ('未选择串口端口或串口断开')

    def gui_downloadHex(self, hexFile, ser):
         with open(hexFile,'r') as hexfile:
            starttime = time.perf_counter()
            # print(' *Hex file : \''+self.filePath+'\''+' is opened*')
            for line in hexfile.readlines():
                if(line[0] == ':'):
                    line_b = bytes(line, 'utf-8')
                    # line_b = self.char2hex(line)
                    print ('send data is ' + str(line_b))
                    ser.write(line_b)  # 写入字符串
                    line_read = ser.readline()   # read a '\n' terminated line
                    print ('read serial data: ' + str(line_read))
                    # if ser.in_waiting:
                    #     readbuf = ser.read(ser.in_waiting)
                    #     while (readbuf[0] ):
                    #         pass
                    #     print (readbuf)
                    self.hexDownloadLine += 1
                    self.valProcessBar.set(self.hexDownloadLine / self.hexFileLines * 100)
            endtime = time.perf_counter()
            print(' Time elapsed:' + str(endtime-starttime))
            hexfile.close()
  

    def char2hex(self, line):
        line = list(map(ord, list(line)))
        for num in range(len(line)):
            if line[num]>=0x30 and line[num]<=0x39:
                line[num] = line[num] - 0x30
            elif line[num]>=0x41 and line[num]<=0x5A:
                line[num] = line[num] - 55
            else:
                pass
        line=line[1:-1]     #delete CR and ':', in terms of byte
        for i in range(0, len(line), 2):
            line[i] = line[i]*16 + line[i+1]
            newline = line[::2]
        return newline

class serial_class:
    def __init__(self):
        pass

    def serial_findPort(self):
        self.plist = list(serial.tools.list_ports.comports())
        self.comList = {}
        if len(self.plist) <= 0:
            print("The Serial port can't find!")
            pass
        else:
            for i in range(len(self.plist)):
                self.plist[i] = list(self.plist[i])
                self.comList[self.plist[i][0]] = self.plist[i][1]
        return self.comList

if __name__ == "__main__":
    gui_app = gui_class()
    gui_app.gui_running()
